#include "CCalculations.h"

#include <QDebug>
#include "../CVMDataTypes.h"
#include "../CCoreException.h"
#include "../data/CFunction.h"

void addSubMulDiv(CFrame& frame, COperationCodes opcode)
{
    if (frame.operandCount() < 2)
        throw CCoreException(CCoreException::VM_EXCEPTION_INSUFFICIENT_OPERANDS, "<operations.arithmetic.add>");

    char* oprnd2 = frame.popOperand();
    char* oprnd1 = frame.popOperand();
    char* rslt;

    switch((CVMDataTypes)*oprnd1)
    {
        case CVMDataTypes::NUMBER:
        {
            switch((CVMDataTypes)*oprnd2)
            {
                case CVMDataTypes::NUMBER:
                {
                    rslt = new char[sizeof(double) + 1];
                    *rslt = (char)CVMDataTypes::NUMBER;
                    switch(opcode)
                    {
                        case COperationCodes::ADD:
                        {
                            setNumber(getNumber(oprnd1) + getNumber(oprnd2), rslt);
                            break;
                        }
                        case COperationCodes::SUB:
                        {
                            setNumber(getNumber(oprnd1) - getNumber(oprnd2), rslt);
                            break;
                        }
                        case COperationCodes::MUL:
                        {
                            setNumber(getNumber(oprnd1) * getNumber(oprnd2), rslt);
                            break;
                        }
                        case COperationCodes::DIV:
                        {
                            setNumber(getNumber(oprnd1) / getNumber(oprnd2), rslt);
                            break;
                        }
                        default:
                            throw CCoreException(CCoreException::VM_EXCEPTION_WRONG_OPERATION, "<operations.arithmetic.add>");
                    }
                    break;
                }
                default:
                    throw CCoreException(CCoreException::VM_EXCEPTION_WRONG_OPERAND, "<operations.arithmetic.add>");
            }
            break;
        }
        default:
            throw CCoreException(CCoreException::VM_EXCEPTION_WRONG_OPERAND, "<operations.arithmetic.add>");
    }

    frame.pushOperand(rslt);
    delete[] oprnd1;
    delete[] oprnd2;
}

void egNeqGteGtLteLt(CFrame& frame, COperationCodes opcode)
{
    if (frame.operandCount() < 2)
        throw CCoreException(CCoreException::VM_EXCEPTION_INSUFFICIENT_OPERANDS, "<operations.boolean.egNeqGteGtLteLt>");

    char* oprnd2 = frame.popOperand();
    char* oprnd1 = frame.popOperand();

    switch(opcode)
    {
        case COperationCodes::EQ:
        {
            frame.pushChar((char)(compare(oprnd1, oprnd2) == 0 ? CVMDataTypes::TRUE : CVMDataTypes::FALSE));
            break;
        }
        case COperationCodes::NEQ:
        {
            frame.pushChar((char)(compare(oprnd1, oprnd2) != 0 ? CVMDataTypes::TRUE : CVMDataTypes::FALSE));
            break;
        }
        case COperationCodes::GTE:
        {
            frame.pushChar((char)(compare(oprnd1, oprnd2) >= 0 ? CVMDataTypes::TRUE : CVMDataTypes::FALSE));
            break;
        }
        case COperationCodes::GT:
        {
            frame.pushChar((char)(compare(oprnd1, oprnd2) > 0 ? CVMDataTypes::TRUE : CVMDataTypes::FALSE));
            break;
        }
        case COperationCodes::LTE:
        {
            frame.pushChar((char)(compare(oprnd1, oprnd2) <= 0 ? CVMDataTypes::TRUE : CVMDataTypes::FALSE));
            break;
        }
        case COperationCodes::LT:
        {
            frame.pushChar((char)(compare(oprnd1, oprnd2) < 0 ? CVMDataTypes::TRUE : CVMDataTypes::FALSE));
            break;
        }
        default:
            throw CCoreException(CCoreException::VM_EXCEPTION_WRONG_OPERATION, "<operations.boolean.egNeqGteGtLteLt>");
    }

    delete[] oprnd1;
    delete[] oprnd2;
}

void orAnd(CFrame& frame, COperationCodes opcode)
{
    if (frame.operandCount() < 2)
        throw CCoreException(CCoreException::VM_EXCEPTION_INSUFFICIENT_OPERANDS, "<operations.boolean.egNeqGteGtLteLt>");

    char* oprnd2 = frame.popOperand();
    char* oprnd1 = frame.popOperand();

    bool rslt1 = isTrue(oprnd1);
    bool rslt2 = isTrue(oprnd2);

    switch(opcode)
    {
        case COperationCodes::OR:
        {
            if (rslt1 || rslt2) frame.pushTrue();
            else frame.pushFalse();
            break;
        }
        case COperationCodes::AND:
        {
            if (rslt1 && rslt2) frame.pushTrue();
            else frame.pushFalse();
            break;
        }
        default:
            throw CCoreException(CCoreException::VM_EXCEPTION_WRONG_OPERATION, "<operations.boolean.egNeqGteGtLteLt>");
    }

    delete[] oprnd1;
    delete[] oprnd2;
}

void not_(CFrame& frame)
{
    if (frame.operandCount() < 1)
        throw CCoreException(CCoreException::VM_EXCEPTION_INSUFFICIENT_OPERANDS, "<operations.boolean.not>");

    char* oprnd = frame.popOperand();

    if (isTrue(oprnd)) frame.pushFalse();
    else frame.pushFalse();

    delete oprnd;
}
